package com.lyy.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.DesensitizedUtil;
import cn.hutool.core.util.IdcardUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.SecureUtil;
import com.lyy.entity.User;
import com.lyy.mapper.UserMapper;
import com.lyy.service.UserService;
import com.lyy.util.BuildUtil;
import com.lyy.util.MinioUtil;
import com.lyy.util.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author Liangyaoyun
 */
@Service
@Slf4j
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private RedisUtil redisUtil;
    private final String   VCODE_KEY_PREFIX="lesson:vcode:phone-";
    private final String POINTS_KEY_PREFIX="lesson:points:user-";

    @Override
    public User register(User user) {

        User condition;
        List<User> users;

        // DB: 检查 [账号] 是否已存在
        condition = new User();
        condition.setUsername(user.getUsername());
        condition.setUserDeleted(0);
        users = userMapper.select(condition);
        if (ObjectUtil.isNotEmpty(users)) {
            log.error("用户注册失败: 账号 {} 已存在", user.getUsername());
            throw new RuntimeException("账号已存在");
        }

        // DB: 检查 [手机号码] 是否已存在
        condition = new User();
        condition.setPhone(user.getPhone());
        condition.setUserDeleted(0);
        users = userMapper.select(condition);
        if (ObjectUtil.isNotEmpty(users)) {
            log.error("用户注册失败: 手机号码 {} 已存在", user.getPhone());
            throw new RuntimeException("手机号码已存在");
        }

        // DB: 检查 [身份证号] 是否已存在
        condition = new User();
        condition.setIdCard(user.getIdCard());
        condition.setUserDeleted(0);
        users = userMapper.select(condition);
        if (ObjectUtil.isNotEmpty(users)) {
            log.error("用户注册失败: 身份证号 {} 已存在", user.getIdCard());
            throw new RuntimeException("身份证号已存在");
        }

        // 获取用户身份证号，从身份证号中可推算出性别，年龄，星座，属相等信息
        String idCard = user.getIdCard();
        if (!IdcardUtil.isValidCard(idCard)) {
            log.error("用户注册失败: 身份证号 {} 不合法", user.getIdCard());
            throw new RuntimeException("身份证号不合法");
        }

        // 处理用户密码: 使用MD5加密
        user.setPassword(SecureUtil.md5(user.getPassword()));

        // 设置默认昵称: 使用 "星座+随机10位字符串" 格式作为默认昵称
        int month = IdcardUtil.getMonthByIdCard(idCard);
        int date = IdcardUtil.getDayByIdCard(idCard);
        user.setNickName(DateUtil.getZodiac(month - 1, date) + RandomUtil.randomString(10));

        // 设置默认性别: 根据身份证号自动识别
        user.setGender(IdcardUtil.getGenderByIdCard(idCard));

        // 设置默认年龄: 根据身份证号自动识别
        user.setAge(IdcardUtil.getAgeByIdCard(idCard));

        // 设置默认头像: 使用 "属相.png" 作为默认头像文件
        int year = IdcardUtil.getYearByIdCard(idCard);
        user.setAvatarUrl(DateUtil.getChineseZodiac(year) + ".png");

        // 设置默认描述: 使用 "省份+高级VIP用户" 格式作为默认描述
        user.setUserInfo(IdcardUtil.getProvinceByIdCard(idCard) + "高级VIP用户");
        user.setUserDeleted(0);
        user.setCreateTime(new Date().getTime());
        user.setModifyTime(new Date().getTime());

        // DB: 添加一名 [用户]
        if (userMapper.insert(user) <= 0) {
            log.error("用户注册失败: 用户表数据库记录添加失败");
            throw new RuntimeException("用户添加失败");
        }

        // 将用户记录脱敏后返回
        return this.desensitizationUser(user);
    }
    @Override
    public User loginByAccount(String username, String password) {

        // DB: 按账号密码查询用户记录
        User condition = new User();
        condition.setUsername(username);
        condition.setPassword(SecureUtil.md5(password));
        condition.setUserDeleted(0);
        List<User> users = userMapper.select(condition);
        if (ObjectUtil.isEmpty(users)) {
            throw new RuntimeException("用户登录失败: 用户账号不存在或用户密码错误");
        }

        // 将用户记录脱敏后返回
        return this.desensitizationUser(users.get(0));
    }

    @Override
    public User loginByPhone(String vcode, String phone) {
        String key=VCODE_KEY_PREFIX+phone;
        if (!redisUtil.exists(key)){
            throw new RuntimeException("验证码已经失效！");
        }
        if (!vcode.equals(redisUtil.get(key))){
            throw new RuntimeException("验证码错误!");
        }
        User condition=new User();
        condition.setPhone(phone);
        condition.setUserDeleted(0);
        List<User>users=userMapper.select(condition);
        if (ObjectUtil.isEmpty(users)){
            throw new RuntimeException("手机号不存在!");
        }
        //登录成功后删除验证码记录
        redisUtil.del(key);
        //脱敏返回
        return this.desensitizationUser(users.get(0));
    }

    @Override
    public String generateVcode(String phone) {
        User condition=new User();
        condition.setPhone(phone);
        condition.setUserDeleted(0);

        List<User>users=userMapper.select(condition);
        if (ObjectUtil.isEmpty(users)){
            throw new RuntimeException("手机号不存在");
        }
        String key=VCODE_KEY_PREFIX+phone;
        String val= RandomUtil.randomNumbers(4);

        redisUtil.setEx(key,val, 5,TimeUnit.MINUTES);
        return val;
    }

    private User desensitizationUser(User user) {
        user.setIdCard(DesensitizedUtil.idCardNum(user.getIdCard(), 6, 3));
        user.setPhone(DesensitizedUtil.mobilePhone(user.getPhone()));
        user.setRealName(DesensitizedUtil.chineseName(user.getRealName()));
        user.setPassword("");
        return user;
    }

    @Override
    public User selectByUserId(Integer userId) {

        // DB: 按用户主键查询用户记录
        User condition = new User();
        condition.setUserId(userId);
        condition.setUserDeleted(0);
        List<User> users = userMapper.select(condition);
        if (ObjectUtil.isEmpty(users)) {
            throw new RuntimeException("用户查询失败: 用户主键不存在");
        }

        // 将用户记录脱敏后返回
        return this.desensitizationUser(users.get(0));
    }

    @Override
    public User selectByPhone(String phone) {

        // DB: 按手机号码查询用户记录
        User condition = new User();
        condition.setPhone(phone);
        condition.setUserDeleted(0);
        List<User> users = userMapper.select(condition);
        if (ObjectUtil.isEmpty(users)) {
            throw new RuntimeException("用户查询失败: 用户手机号码不存在");
        }

        // 将用户记录脱敏后返回
        return this.desensitizationUser(users.get(0));
    }

    @Override
    public void updateByUserId(User user, Integer userId) {
        User condition;
        // DB: 检查 [用户昵称] 是否已存在
        if(user.getNickName() != null){
            String nickName = user.getNickName();
            condition = new User();
            condition.setNickName(nickName);
            List<User> users = userMapper.select(condition);
            if (ObjectUtil.isNotEmpty(users)) {
                log.error("用户修改失败: 用户昵称 {} 已存在", nickName);
                throw new RuntimeException("昵称已存在");
            }
        }
        // DB: 按用户主键修改User记录
        user.setUserId(null);
        user.setUsername(null);
        user.setCreateTime(null);
        user.setModifyTime(new Date().getTime());
        User condition1 = new User();
        condition1.setUserId(userId);
        condition1.setUserDeleted(0);
        int result = userMapper.update(user, condition1);
        if (result <= 0) {
            throw new RuntimeException("用户修改失败: 未知原因");
        }
    }

    @Override
    public void deleteByUserId(Integer userId) {

        // DB: 按用户主键删除用户记录（假删）
        User user = new User();
        user.setUserDeleted(1);
        this.updateByUserId(user, userId);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void uploadAvatar(MultipartFile avatarFile, Integer userId) {

        // DB: 按用户表主键查询User记录
        User condition = new User();
        condition.setUserId(userId);
        condition.setUserDeleted(0);
        List<User> users = userMapper.select(condition);
        if (ObjectUtil.isEmpty(users)) {
            throw new RuntimeException("上传头像失败: 用户表主键不存在");
        }

        // 从MinIO中删除用户的旧头像: 默认头像不删除
        String avatarUrl = users.get(0).getAvatarUrl();
        List<String>defaultAvatars= Arrays.asList(
                "鼠.png","牛.png","虎.png","兔.png","龙.png","蛇.png","马.png",
                "羊.png","猴.png","鸡.png","狗.png","猪.png");
        if (!defaultAvatars.contains(avatarUrl)) {
            MinioUtil.delete(avatarUrl, "avatar");
        }

        // 向MinIO中上传用户的新头像
        String newAvatarUrl = MinioUtil.upload(avatarFile, "avatar");

        // DB: 修改用户头像信息
        User user = new User();
        user.setAvatarUrl(newAvatarUrl);
        condition = new User();
        condition.setUserId(userId);
        condition.setUserDeleted(0);
        if (userMapper.update(user, condition) <= 0) {
            throw new RuntimeException("上传头像失败: 用户头像数据库记录修改失败");
        }
    }

    @Override
    public Double selectPoints(Integer userId) {
        String key = POINTS_KEY_PREFIX + userId;
        Integer num = Integer.parseInt(redisUtil.get(key));
        Double result = num.doubleValue();
        return result;
    }

    @Override
    public void updatePassword(Integer userId, String oldPassword, String newPassword) {

        // DB: 按用户主键和旧密码查询用户记录
        User condition = new User();
        condition.setUserId(userId);
        condition.setPassword(SecureUtil.md5(oldPassword));
        List<User> users = userMapper.select(condition);
        if (ObjectUtil.isEmpty(users)) {
            throw new RuntimeException("修改密码失败: 用户不存在或用户密码错误");
        }

        // DB: 按用户主键修改密码
        User user = new User();
        user.setPassword(SecureUtil.md5(newPassword));
        this.updateByUserId(user, userId);
    }
}
